from copy import deepcopy
from pathlib import Path
from typing import Any, Dict, Optional, Sequence, Union

from mmengine.fileio import get_file_backend, isdir, join_path, list_dir_or_file
from mmengine.structures import InstanceData
from mmdet.structures import DetDataSample
import numpy as np

# from ..preprocess import ImagePreprocessor


def create_data_sample(data: Dict[str, Any], **kwargs) -> DetDataSample:
    """Convert all input arguments to a DetDataSample instance.

    Args:
        data (Dict[str, Any]): A dict containing some image meta informations.
            it's key must be string, like 'img_path', 'ori_shape', 'img_shape'.
        **kwargs: Some supplementary information. like `img_id=0`.

    Returns:
        DetDataSample: return a DetDataSample instance.
    """
    data_ = deepcopy(data)
    data_sample = DetDataSample(metainfo=data_)
    data_sample.set_metainfo(kwargs)
    data_sample.gt_instances = InstanceData()
    data_sample.ignored_instances = InstanceData()
    return data_sample


def inputs_to_list(inputs: Union[str, Sequence[str]]) -> list:
    """Process the inputs to a list.

    This implementation is modified from
        mmdetection/tree/v3.0.0/mmdet/apis/det_inferencer.py

    Process inputs to a list according to its type:
    - list or tuple: return inputs
    - str:
        - Directory path: return all files in the directory
        - other cases: return a list containing the string. The string
            could be a path to file, a url or other types of string according
            to the task.

    Args:
        inputs (Union[str, Sequence[str]]): Inputs for the inferencer.

    Returns:
        list: List of input for the :meth:`preprocess`.
    """
    img_extensions = ('.jpg', '.jpeg', '.png', '.ppm', '.bmp', '.pgm',
                      '.tif', '.tiff', '.webp')
    if isinstance(inputs, str):
        backend = get_file_backend(inputs)
        if hasattr(backend, 'isdir') and isdir(inputs):
            # Backends like HttpsBackend do not implement `isdir`, so only
            # those backends that implement `isdir` could accept the inputs
            # as a directory
            filename_list = list_dir_or_file(
                inputs, list_dir=False, suffix=img_extensions)
            inputs = [
                join_path(inputs, filename) for filename in filename_list
            ]

    if not isinstance(inputs, (list, tuple)):
        inputs = [inputs]

    return list(inputs)


class DataHelper:
    """A helper for load data.
    Find all the images and collate to a many batchs. If you need, you can
    Asynchronously preprocess batch images and return results.

    Args:
        inputs (Union[str, Sequence[str]]): image paths or directory path
        batch_size (int): batch size of model input.
    """
    def __init__(self, inputs: Union[str, Sequence[str]], batch_size: int = 1):
        self.batch_size = batch_size
        self.image_list = inputs_to_list(inputs)
        self.batch_list = [self.image_list[i: i+batch_size]
                           for i in range(0, len(self.image_list), batch_size)]

    @property
    def num_images(self):
        return len(self.image_list)

    @property
    def num_batches(self):
        return len(self.batch_list)

    # TODO: Asynchronously provide data for multiple workers.
    def async_load(
                #    data_preprocessor: ImagePreprocessor,
                   data_preprocessor,
                   num_workers: int = 1) -> dict:
        return {}
